import ElementUtils from '@/editor/js/design-view/elements/element-utils'
export default class AppData {

  constructor(options) {
    // initiates the collections
    if (options !== undefined) {
      this.siddhiAppName = options.siddhiAppName;
      this.siddhiAppDescription = options.siddhiAppDescription;
      // Following lists hold references for comments in the code view. These lists are passed to the backend
      // when generating the code from design. Front end does not use them.
      this.elementCodeSegments = options.elementCodeSegments;
      this.commentCodeSegments = options.commentCodeSegments;
    } else {
      this.siddhiAppName = '';
      this.siddhiAppDescription = '';
    }
    this.appAnnotationList = [];
    this.appAnnotationListObjects = [];
    this.streamList = [];
    this.tableList = [];
    this.windowList = [];
    this.triggerList = [];
    this.aggregationList = [];
    this.functionList = [];
    this.partitionList = [];
    this.sourceList = [];
    this.sinkList = [];
    this.queryLists = {
      WINDOW_FILTER_PROJECTION: [],
      PATTERN: [],
      SEQUENCE: [],
      JOIN: []
    };
    // finalElementCount --> Number of elements that exist on the canvas at the time of saving the model
    this.finalElementCount = 0;

  }

  addAppAnnotation(annotation) {
    this.appAnnotationList.push(annotation);
  };
  setAppAnnotationList(appAnnotationList) {
    this.appAnnotationList = appAnnotationList
  };
  addAppAnnotationObject(annotation) {
    this.appAnnotationListObjects.push(annotation);
  };
  setAppAnnotationObjectList(appAnnotationObjectsList) {
    this.appAnnotationListObjects = appAnnotationObjectsList
  };

  addStream(stream) {
    this.streamList.push(stream);
  };
  setStreamList(streamList) {
    this.streamList = streamList
  };
  addTable(table) {
    this.tableList.push(table);
  };
  setTableList(tableList) {
    this.tableList = tableList
  };
  addWindow(window) {
    this.windowList.push(window);
  };
  setWindowList(windowList) {
    this.windowList = windowList
  };
  addTrigger(trigger) {
    this.triggerList.push(trigger);
  };
  setTriggerList(triggerList) {
    this.triggerList = triggerList
  };
  addAggregation(aggregation) {
    this.aggregationList.push(aggregation);
  };
  setAggregationList(aggregationList) {
    this.aggregationList = aggregationList
  };
  addFunction(functionObject) {
    this.functionList.push(functionObject);
  };
  setFunctionList(functionList) {
    this.functionList = functionList
  };
  addWindowFilterProjectionQuery(windowFilterProjectionQuery) {
    this.queryLists.WINDOW_FILTER_PROJECTION.push(windowFilterProjectionQuery);
  };
  setWindowFilterProjectionQueryList(windowFilterProjectionQueryArray) {
    this.queryLists.WINDOW_FILTER_PROJECTION = windowFilterProjectionQueryArray
  }
  addPatternQuery(patternQuery) {
    this.queryLists.PATTERN.push(patternQuery);
  };
  setPatternQueryList(patternQueryArray) {
    this.queryLists.PATTERN = patternQueryArray
  };
  addSequenceQuery(sequenceQuery) {
    this.queryLists.SEQUENCE.push(sequenceQuery);
  };
  setSequenceQueryList(sequenceQueryArray) {
    this.queryLists.SEQUENCE = sequenceQueryArray
  };
  addJoinQuery(joinQuery) {
    this.queryLists.JOIN.push(joinQuery);
  };
  setJoinQueryList(joinQueryArray) {
    this.queryLists.JOIN = joinQueryArray
  };
  addPartition(partition) {
    this.partitionList.push(partition);
  };
  setPartitionList(partitionList) {
    this.partitionList = partitionList
  };
  addSource(source) {
    this.sourceList.push(source);
  };
  setSourceList(sourceList) {
    this.sourceList = sourceList
  };
  addSink(sink) {
    this.sinkList.push(sink);
  };
  // addSequenceQuery(sequenceQuery) {
  //   this.queryLists.SEQUENCE.push(sequenceQuery);
  // };
  addNodeData(objKey, sink) {
    this.queryLists[objKey].push(sink);
  };
  addNodeDataAuth(objKey, sink) {
    this[objKey].push(sink);
  };
  setSinkList(sinkList) {
    this.sinkList = sinkList
  };
  clearAppAnnotationList() {
    ElementUtils.prototype.removeAllElements(this.appAnnotationList);
  };

  clearAppAnnotationListObjects() {
    ElementUtils.prototype.removeAllElements(this.appAnnotationListObjects);
  };

  removeStream(streamId) {
    var isStreamDeleted = ElementUtils.prototype.removeElement(this.streamList, streamId);
    if (!isStreamDeleted) {
      isStreamDeleted = this.removeStreamSavedInsideAPartition(streamId);
    }
    return isStreamDeleted;
  };

  removeTable(tableId) {
    ElementUtils.prototype.removeElement(this.tableList, tableId);
  };

  removeWindow(windowId) {
    ElementUtils.prototype.removeElement(this.windowList, windowId);
  };

  removeTrigger(triggerId) {
    ElementUtils.prototype.removeElement(this.triggerList, triggerId);
  };

  removeAggregation(aggregationId) {
    ElementUtils.prototype.removeElement(this.aggregationList, aggregationId);
  };

  removeFunction(functionId) {
    ElementUtils.prototype.removeElement(this.functionList, functionId);
  };

  removeWindowFilterProjectionQuery(windowFilterProjectionQueryId) {
    var isQueryDeleted = ElementUtils.prototype
      .removeElement(this.queryLists.WINDOW_FILTER_PROJECTION, windowFilterProjectionQueryId);
    if (!isQueryDeleted) {
      isQueryDeleted =
        this.removeQuerySavedInsideAPartition(windowFilterProjectionQueryId,
          'WINDOW_FILTER_PROJECTION_QUERY');
    }
    return isQueryDeleted;
  };

  removePatternQuery(patternQueryId) {
    var isQueryDeleted = ElementUtils.prototype.removeElement(this.queryLists.PATTERN, patternQueryId);
    if (!isQueryDeleted) {
      isQueryDeleted = this.removeQuerySavedInsideAPartition(patternQueryId, 'PATTERN_QUERY');
    }
    return isQueryDeleted;
  };

  removeSequenceQuery(sequenceQueryId) {
    var isQueryDeleted = ElementUtils.prototype.removeElement(this.queryLists.SEQUENCE, sequenceQueryId);
    if (!isQueryDeleted) {
      isQueryDeleted = this.removeQuerySavedInsideAPartition(sequenceQueryId, 'SEQUENCE_QUERY');
    }
    return isQueryDeleted;
  };

  removeJoinQuery(joinQueryId) {
    var isQueryDeleted = ElementUtils.prototype.removeElement(this.queryLists.JOIN, joinQueryId);
    if (!isQueryDeleted) {
      isQueryDeleted = this.removeQuerySavedInsideAPartition(joinQueryId, 'JOIN_QUERY');
    }
    return isQueryDeleted;
  };

  removePartition(partitionId) {
    ElementUtils.prototype.removeElement(this.partitionList, partitionId);
  };

  removeSource(sourceId) {
    ElementUtils.prototype.removeElement(this.sourceList, sourceId);
  };

  removeSink(sinkId) {
    ElementUtils.prototype.removeElement(this.sinkList, sinkId);
  };

  setFinalElementCount(finalElementCount) {
    this.finalElementCount = finalElementCount;
  };

  getSiddhiAppName() {
    return this.siddhiAppName;
  };

  getSiddhiAppDescription() {
    return this.siddhiAppDescription;
  };

  getStream(streamId) {
    var returnedElement = ElementUtils.prototype.getElement(this.streamList, streamId);
    if (!returnedElement) {
      returnedElement = this.getStreamSavedInsideAPartition(streamId);
    }
    return returnedElement;
  };
  getWindowList2(tableId) {
    return ElementUtils.prototype.getElement(this.windowList, tableId);
  }
  getTable(tableId) {
    return ElementUtils.prototype.getElement(this.tableList, tableId);
  };

  getWindow(windowId) {
    return ElementUtils.prototype.getElement(this.windowList, windowId);
  };

  getTrigger(triggerId) {
    return ElementUtils.prototype.getElement(this.triggerList, triggerId);
  };

  getAggregation(aggregationId) {
    return ElementUtils.prototype.getElement(this.aggregationList, aggregationId);
  };

  getFunction(functionId) {
    return ElementUtils.prototype.getElement(this.functionList, functionId);
  };

  getWindowFilterProjectionQuery(windowFilterProjectionQueryId) {
    var returnedElement = ElementUtils.prototype
      .getElement(this.queryLists.WINDOW_FILTER_PROJECTION, windowFilterProjectionQueryId);
    if (!returnedElement) {
      returnedElement =
        this.getQuerySavedInsideAPartition(windowFilterProjectionQueryId, 'WINDOW_FILTER_PROJECTION_QUERY');
    }
    return returnedElement;
  };

  getPatternQuery(patternQueryId) {
    var returnedElement = ElementUtils.prototype.getElement(this.queryLists.PATTERN, patternQueryId);
    if (!returnedElement) {
      returnedElement = this.getQuerySavedInsideAPartition(patternQueryId, 'PATTERN_QUERY');
    }
    return returnedElement;
  };

  getSequenceQuery(sequenceQueryId) {
    var returnedElement = ElementUtils.prototype.getElement(this.queryLists.SEQUENCE, sequenceQueryId);
    if (!returnedElement) {
      returnedElement = this.getQuerySavedInsideAPartition(sequenceQueryId, 'SEQUENCE_QUERY');
    }
    return returnedElement;
  };

  getJoinQuery(joinQueryId) {
    var returnedElement = ElementUtils.prototype.getElement(this.queryLists.JOIN, joinQueryId);
    if (!returnedElement) {
      returnedElement = this.getQuerySavedInsideAPartition(joinQueryId, 'JOIN_QUERY');
    }
    return returnedElement;
  };

  getPartition(partitionId) {
    return ElementUtils.prototype.getElement(this.partitionList, partitionId);
  };

  getSource(sourceId) {
    return ElementUtils.prototype.getElement(this.sourceList, sourceId);
  };

  getSink(sinkId) {
    return ElementUtils.prototype.getElement(this.sinkList, sinkId);
  };

  getAppAnnotationList() {
    return this.appAnnotationList;
  };

  getAppAnnotationListObjects() {
    return this.appAnnotationListObjects;
  };


  getStreamList() {
    return this.streamList;
  };
  getNodeDataOther(key) {
    return this[key];
  };

  getTableList() {
    return this.tableList;
  };

  getWindowList() {
    return this.windowList;
  };

  getTriggerList() {
    return this.triggerList;
  };

  getAggregationList() {
    return this.aggregationList;
  };

  getFunctionList() {
    return this.functionList;
  };
  getNodeData(key) {
    return this.queryLists[key];
  };
  getWindowFilterProjectionQueryList() {
    return this.queryLists.WINDOW_FILTER_PROJECTION;
  };

  getPatternQueryList() {
    return this.queryLists.PATTERN;
  };

  getSequenceQueryList() {
    return this.queryLists.SEQUENCE;
  };

  getJoinQueryList() {
    return this.queryLists.JOIN;
  };

  getPartitionList() {
    return this.partitionList;
  };

  getSourceList() {
    return this.sourceList;
  };

  getSinkList() {
    return this.sinkList;
  };

  getFinalElementCount() {
    return this.finalElementCount;
  };

  setSiddhiAppName(siddhiAppName) {
    this.siddhiAppName = siddhiAppName;
  };

  setSiddhiAppDescription(siddhiAppDescription) {
    this.siddhiAppDescription = siddhiAppDescription;
  };

  setAppAnnotationList(appAnnotationList) {
    this.appAnnotationList = appAnnotationList;
  };

  setAppAnnotationListObjects(appAnnotationListObjects) {
    this.appAnnotationListObjects = appAnnotationListObjects;
  };


  /**
   * @function Get the element by providing the element id
   * @param elementId id of the definition element
   * @param includeQueryTypes if true search in the queries as well
   * @param includeSourceAndSink if true search in the sinks and sources as well
   * @param includePartitions if true search in the partition list as well
   * @return requestedElement returns undefined if the requested element is not found
   */
  getDefinitionElementById(elementId, includeQueryTypes, includeSourceAndSink,
    includePartitions) {
    var self = this;
    var requestedElement;
    var streamList = self.streamList;
    var tableList = self.tableList;
    var windowList = self.windowList;
    var aggregationList = self.aggregationList;
    var functionList = self.functionList;
    var triggerList = self.triggerList;
    var windowFilterProjectionQueryList = self.queryLists.WINDOW_FILTER_PROJECTION;
    var PATTERN = self.queryLists.PATTERN;
    var SEQUENCE = self.queryLists.SEQUENCE;
    var JOIN = self.queryLists.JOIN;
    // var JOIN = self.queryLists;
    var sourceList = self.sourceList;
    var sinkList = self.sinkList;
    var partitionList = self.partitionList;

    var lists = [streamList, tableList, windowList, aggregationList, functionList, triggerList];

    if (includeQueryTypes !== undefined && includeQueryTypes) {
      lists.push(windowFilterProjectionQueryList);
      lists.push(PATTERN);
      lists.push(SEQUENCE);
      lists.push(JOIN);
    }
    if (includeSourceAndSink !== undefined && includeSourceAndSink) {
      lists.push(sourceList);
      lists.push(sinkList);
    }
    if (includePartitions !== undefined && includePartitions) {
      lists.push(partitionList);
    }

    _.forEach(lists, function (list) {
      _.forEach(list, function (element) {
        if (element.getId() === elementId) {
          var type = '';
          if (list === streamList) {
            type = 'STREAM';
          } else if (list === tableList) {
            type = 'TABLE';
          } else if (list === windowList) {
            type = 'WINDOW';
          } else if (list === aggregationList) {
            type = 'AGGREGATION';
          } else if (list === functionList) {
            type = 'FUNCTION';
          } else if (list === triggerList) {
            type = 'TRIGGER';
          } else if (list === windowFilterProjectionQueryList) {
            type = 'WINDOW_FILTER_PROJECTION_QUERY';
          } else if (list === PATTERN) {
            type = 'PATTERN_QUERY';
          } else if (list === SEQUENCE) {
            type = 'SEQUENCE_QUERY';
          } else if (list === JOIN) {
            type = 'JOIN_QUERY';
          } else if (list === sourceList) {
            type = 'SOURCE';
          } else if (list === sinkList) {
            type = 'SINK';
          } else if (list === partitionList) {
            type = 'PARTITION';
          }
          requestedElement = {
            type: type,
            element: element
          };
        }
      });
    });

    // check the element in queries inside the partitions
    if (includeQueryTypes !== undefined && includeQueryTypes && !requestedElement) {
      requestedElement = self.getQueryByIdSavedInsideAPartition(elementId);
    }
    // search in the inner streams in partitions
    if (!requestedElement) {
      var element = self.getStreamSavedInsideAPartition(elementId);
      if (element !== undefined) {
        requestedElement = {
          type: 'STREAM',
          element: element
        };
      }
    }

    return requestedElement;
  };

  /**
   * @function Get the element by providing the element name, If partitionId parameter is provided the provided
   * element name will be searched inside the given partition as well.
   * @param elementName name of the definition element
   * @param partitionId partition Id which the element needed to be searched
   * @return requestedElement returns undefined if the requested element is not found
   */
  getDefinitionElementByName(elementName, partitionId) {
    var self = this;
    var requestedElement;
    var streamList = self.streamList;
    var tableList = self.tableList;
    var windowList = self.windowList;
    var aggregationList = self.aggregationList;
    var functionList = self.functionList;
    var triggerList = self.triggerList;

    var listNames = [streamList, tableList, windowList, aggregationList, functionList, triggerList];

    _.forEach(listNames, function (list) {
      _.forEach(list, function (element) {
        if (element.getName() === elementName) {
          var type = '';
          if (list === streamList) {
            type = 'STREAM';
          } else if (list === tableList) {
            type = 'TABLE';
          } else if (list === windowList) {
            type = 'WINDOW';
          } else if (list === aggregationList) {
            type = 'AGGREGATION';
          } else if (list === functionList) {
            type = 'FUNCTION';
          } else if (list === triggerList) {
            type = 'TRIGGER';
          }
          requestedElement = {
            type: type,
            element: element
          };
        }
      });
    });

    // check the element in the given partition
    if (!requestedElement && partitionId !== undefined) {
      var partition = self.getPartition(partitionId);
      // Only stream elements are in the partition which has a name attribute. So we search name only in
      // streamsList.
      var element = partition.getStreamByName(elementName);
      requestedElement = {
        type: 'STREAM',
        element: element
      };
    }

    return requestedElement;
  };

  /**
   * @function Checks whether a given query is inside a partition and if yes it returns
   * @param queryId id of the query element
   * @param queryType type of the query
   * @return requestedElement returns undefined if the requested element is not found. Otherwise returns the
   * requestedElement
   */
  getQuerySavedInsideAPartition(queryId, queryType) {
    var self = this;
    var requestedElement;
    _.forEach(self.partitionList, function (partition) {
      if (!requestedElement) {
        if (queryType === 'WINDOW_FILTER_PROJECTION_QUERY') {
          requestedElement = partition.getWindowFilterProjectionQuery(queryId);
        } else if (queryType === 'PATTERN_QUERY') {
          requestedElement = partition.getPatternQuery(queryId);
        } else if (queryType === 'SEQUENCE_QUERY') {
          requestedElement = partition.getSequenceQuery(queryId);
        } else if (queryType === 'JOIN_QUERY') {
          requestedElement = partition.getJoinQuery(queryId);
        }
      }
    });
    return requestedElement;
  };

  /**
   * @function Checks whether a given stream is inside a partition and if yes it returns
   * @param streamId id of the query element
   * @return requestedElement returns undefined if the requested element is not found. Otherwise returns the
   * requestedElement
   */
  getStreamSavedInsideAPartition(streamId) {
    var self = this;
    var requestedElement;
    _.forEach(self.partitionList, function (partition) {
      if (!requestedElement) {
        requestedElement = partition.getStream(streamId);
      }
    });
    return requestedElement;
  };

  /**
   * @function Checks whether a given query is inside a partition and if yes removes it
   * @param queryId id of the query element
   * @param queryType type of the query
   * @return boolean returns false if the element is not found.
   */
  removeQuerySavedInsideAPartition(queryId, queryType) {
    var self = this;
    var isQueryDeleted = false;
    _.forEach(self.partitionList, function (partition) {
      if (!isQueryDeleted) {
        if (queryType === 'WINDOW_FILTER_PROJECTION_QUERY') {
          isQueryDeleted = partition.removeWindowFilterProjectionQuery(queryId);
        } else if (queryType === 'PATTERN_QUERY') {
          isQueryDeleted = partition.removePatternQuery(queryId);
        } else if (queryType === 'SEQUENCE_QUERY') {
          isQueryDeleted = partition.removeSequenceQuery(queryId);
        } else if (queryType === 'JOIN_QUERY') {
          isQueryDeleted = partition.removeJoinQuery(queryId);
        }
      }
    });
    return isQueryDeleted;
  };

  /**
   * @function Checks whether a given stream is inside a partition and if yes removes it
   * @param streamId id of the stream element
   * @return boolean returns false if the element is not found.
   */
  removeStreamSavedInsideAPartition(streamId) {
    var self = this;
    var isStreamDeleted = false;
    _.forEach(self.partitionList, function (partition) {
      if (!isStreamDeleted) {
        isStreamDeleted = partition.removeStream(streamId);
      }
    });
    return isStreamDeleted;
  };

  /**
   * @function Checks whether a given query is inside a partition by id and if yes it returns a object with type
   * and element (ex: {type: type, element: element})
   * @param queryId id of the query element
   * @return requestedElement returns undefined if the requested element is not found. Otherwise returns the
   * requestedElement
   */
  getQueryByIdSavedInsideAPartition(queryId) {
    var self = this;
    var element;
    var type;
    _.forEach(self.partitionList, function (partition) {
      if (!element) {
        element = partition.getWindowFilterProjectionQuery(queryId);
        type = 'WINDOW_FILTER_PROJECTION_QUERY';
        if (!element) {
          element = partition.getPatternQuery(queryId);
          type = 'PATTERN_QUERY';
        }
        if (!element) {
          element = partition.getSequenceQuery(queryId);
          type = 'SEQUENCE_QUERY';
        }
        if (!element) {
          element = partition.getJoinQuery(queryId);
          type = 'JOIN_QUERY';
        }
      }
    });

    var requestedElement;
    if (element !== undefined) {
      requestedElement = {
        type: type,
        element: element
      };
    }
    return requestedElement;
  };

  /**
   * @function Checks whether a given query is saved inside a partition and if yes it returns the partition Object
   * @param queryId id of the query element
   * @return requestedElement returns undefined if the requested element is not found. Otherwise returns the
   * requestedElement
   */
  getPartitionWhereQueryIsSaved(queryId) {
    var self = this;
    var requestedElement;
    _.forEach(self.partitionList, function (partition) {
      if (!requestedElement) {
        if (partition.getWindowFilterProjectionQuery(queryId) !== undefined) {
          requestedElement = partition;
        } else if (partition.getPatternQuery(queryId) !== undefined) {
          requestedElement = partition;
        } else if (partition.getSequenceQuery(queryId) !== undefined) {
          requestedElement = partition;
        } else if (partition.getJoinQuery(queryId) !== undefined) {
          requestedElement = partition;
        }
      }
    });

    return requestedElement;
  };

  /**
   * @function Checks whether a given stream is saved inside a partition and if yes it returns the partition
   * Object
   * @param streamId id of the stream element
   * @return requestedElement returns undefined if the requested element is not found. Otherwise returns the
   * requestedElement
   */
  getPartitionWhereStreamIsSaved(streamId) {
    var self = this;
    var requestedElement;
    _.forEach(self.partitionList, function (partition) {
      if (!requestedElement && partition.getStream(streamId) !== undefined) {
        requestedElement = partition;
      }
    });

    return requestedElement;
  };
}
